/* Standard includes. */
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "fr30xx.h"

/* FreeRTOS kernel includes. */
#include "FreeRTOS.h"
#include "task.h"

/* LVGL includes */
#include "lvgl.h"

#include "fdb_app.h"
#include "host.h"

//#include "ff.h"
//#include "elog.h"

//#include "audio_scene.h"

/* peripheral drivers */
#include "fr30xx.h"
#include "driver_flash.h"
#include "IC_W25Qxx.h"
#include "FreeRTOS.h"

#include "app_config.h"
#include "app_task.h"
#include "app_at.h"
#include "app_hw.h"
#include "app_rpmsg.h"
//#include "fr_device_pmu_io.h"
//#include "fr_device_button.h"
#include "host.h"
#include "driver_pmu_iwdt.h"
//#include "fr_device_rtc.h"
//#include "fr_device_vbat.h"
#include "ext_flash.h"
#include "ext_flash_uart.h"
#include "ext_flash_program.h"
#include "heap.h"
#include "driver_display.h"
#include "driver_display_dev.h"
#include "system_dwt.h"

/* hardware handlers */
static UART_HandleTypeDef Uart3_handle;
static CALI_HandleTypeDef cali_handle;
SD_HandleTypeDef sdio_handle;
/* file system */
//static FATFS fs;

#if ENABLE_RTOS_MONITOR == 1
/* FreeRTOS running status monitor task */
static TaskHandle_t monitor_task_handle;
volatile unsigned int CPU_RunTime;
static uint8_t CPU_RunInfo[2048];
#endif

/* APP task */
TaskHandle_t app_task_handle;

static enum_PinStatus_t current_PD0_level = GPIO_PIN_SET;

void controller_start(void);
void host_start(void);

#if defined(__ARMCC_VERSION) || defined(__CC_ARM)
int fputc(int c, FILE *fp)
{
    uart_transmit(&Uart3_handle, (void *)&c, 1);

    return c;
}
#endif

#ifdef __GNUC__
int _write(int file, char *ptr, int len)
{
    uart_transmit(&Uart3_handle, (void *)ptr, len);
    while(!(Uart3_handle.UARTx->USR.TFE));
    return len;
}
#endif

#ifdef __ICCARM__
int putchar(int c)
{
    uart_transmit(&Uart3_handle, (void *)&c, 1);
    while(!(Uart3_handle.UARTx->USR.TFE));
    return c;
}
#endif

static const struct ext_flash_operator_t ext_flash_op = {
   .flash_init = ext_flash_get_id,
   .read = ext_flash_read,
   .write = ext_flash_write,
   .erase = ext_flash_erase,
   .chip_erase = NULL,
   .protect_disable = NULL,
   .protect_enable = NULL,
};

static const struct ext_flash_prog_uart_op_t ext_flash_prog_uart_op = {
   .init = prog_uart_init,
   .read = prog_uart_read,
   .read_no_block = prog_uart_read_no_block,
   .write = prog_uart_write,
};

void sys_wdt_init(void)
{
    iwdt_Init_t iwdt_handle;
    iwdt_handle.iwdt_int_Enable = WDT_INT_DISABLE;
    iwdt_handle.iwdt_Timeout    = 0xFFF;
    iwdt_handle.iwdt_Count      = 32000 * 61;     // 32K, timeout 3s
    iwdt_init(iwdt_handle);
    
    iwdt_Enable();
}

#if ENABLE_RTOS_MONITOR == 1
static void monitor_task(void *arg)
{
   vTaskDelay(5000);
   
   /* enable sleep */
   system_prevent_sleep_clear(SYSTEM_PREVENT_SLEEP_TYPE_DISABLE);

   sys_wdt_init();

    while(1) {
       vTaskDelay(20000);
       iwdt_Refresh();
       rtc_running();
        //battery detect
        //adc_vbat_start_detect();

        // memset(CPU_RunInfo,0,2048);
        // vTaskList((char *)&CPU_RunInfo);
        // printf("---------------------------------------------\r\n");
        // printf("name           	state     priority  stack     seq\r\n");
        // printf("%s", CPU_RunInfo);
        // printf("---------------------------------------------\r\n");
        
        // memset(CPU_RunInfo,0,400);
        // vTaskGetRunTimeStats((char *)&CPU_RunInfo);
        // printf("name                counter             usage\r\n");
        // printf("%s", CPU_RunInfo);
        // printf("---------------------------------------------\r\n");
    }
}
#endif

static void cali_done_handle(CALI_HandleTypeDef *hcali, uint32_t result)
{
    system_set_LPRCCLK(cali_calc_rc_freq(hcali, result));
    system_prevent_sleep_clear(SYSTEM_PREVENT_SLEEP_TYPE_CALIBRATION);
}

void spi_flash_cs_set(void)
{
    gpio_write_pin(GPIOD, GPIO_PIN_1, GPIO_PIN_SET);
}

void spi_flash_cs_clear(void)
{
    gpio_write_pin(GPIOD, GPIO_PIN_1, GPIO_PIN_CLEAR);
}

void vApplicationStackOverflowHook( TaskHandle_t pxTask, char *pcTaskName )
{
	( void ) pcTaskName;
	( void ) pxTask;

    //log_i("%s", pcTaskName);
	assert( 0 );
}

void vApplicationTickHook(void)
{
    //gui tick counter add here!
    lv_tick_inc(1);
#if ENABLE_RTOS_MONITOR == 1
    CPU_RunTime++;
    #endif
}
__RAM_CODE bool user_deep_sleep_check(void)
{
    return host_before_sleep_check();
}

__RAM_CODE void user_entry_before_sleep(void)
{
    ool_write16(PMU_REG_PIN_PULL_EN, 0x3fff);
    ool_write16(PMU_REG_PIN_PULL_SEL, 0x3ffd);
    fputc('s', NULL);
    /* wait for UART TX done */
    system_delay_us(20);

    hw_gpio_save();
    ool_write(PMU_REG_PMU_GATE_M, ool_read(PMU_REG_PMU_GATE_M) | 0x40);
}

void wdt_rst_start(void)
{
    // iwdt_Init_t iwdt_handle;
    // iwdt_handle.iwdt_int_Enable = WDT_INT_DISABLE;
    // iwdt_handle.iwdt_Timeout    = 0xFFF;
    // iwdt_handle.iwdt_Count      = 32000 * 3;     // 32K, timeout 3s
    // iwdt_init(iwdt_handle);
    // iwdt_Enable();

    __disable_irq();
    // reboot
    iwdt_Init_t iwdt_env;
    iwdt_env.iwdt_Count = 300;
    iwdt_env.iwdt_Timeout = 10;
    iwdt_env.iwdt_int_Enable = WDT_INT_DISABLE;
    iwdt_init(iwdt_env);
    iwdt_Enable();

//    ool_write(PMU_REG_IOLDO1_CTRL_0, (ool_read(PMU_REG_IOLDO1_CTRL_0) & 0xf0) | 0x01);
    while(1);

}

__RAM_CODE void user_entry_after_sleep(void)
{
    /* 
     * enable pull up of all 3.3v IO, these configuration will be latched by set 
     * BIT6 of PMU_REG_PMU_GATE_M regsiter. used to avoid electric leakage
     */
    SYSTEM->PortA_PullSelect = 0x0000ffff;
    SYSTEM->PortB_PullSelect = 0x0000fff7;
    SYSTEM->PortC_PullSelect = 0x0000ffff;
    SYSTEM->PortD_PullSelect = 0x0000ffff;
    SYSTEM->PortE_PullSelect = 0x0000ffff;
    SYSTEM->PortA_PullEN = 0x0000ffff;
    SYSTEM->PortB_PullEN = 0x0000ffff;
    SYSTEM->PortC_PullEN = 0x0000ffff;
    SYSTEM->PortD_PullEN = 0x0000ffff;
    SYSTEM->PortE_PullEN = 0x0000ffff;
    SYSTEM->QspiPadConfig.QSPI_PullEN = 0x0000000;
    
    SYSTEM->PortA_InputOpenCircuit = 0xFFFFFFFF;
    SYSTEM->PortB_InputOpenCircuit = 0xFFFFFFFF;
    SYSTEM->PortC_InputOpenCircuit = 0xFFFFFFFF;
    SYSTEM->PortD_InputOpenCircuit = 0xFFFFFFFF;
    SYSTEM->PortE_InputOpenCircuit = 0xFFFFFFFF;

    /* configure all interrupt priority to 2 */
    *(volatile uint32_t *)0xE000E400 = 0x40404040;
    *(volatile uint32_t *)0xE000E404 = 0x40404040;
    *(volatile uint32_t *)0xE000E408 = 0x40404040;
    *(volatile uint32_t *)0xE000E40C = 0x40404040;
    *(volatile uint32_t *)0xE000E410 = 0x40404040;
    *(volatile uint32_t *)0xE000E414 = 0x40404040;
    *(volatile uint32_t *)0xE000E418 = 0x40404040;
    *(volatile uint32_t *)0xE000E41C = 0x40404040;
    *(volatile uint32_t *)0xE000E420 = 0x40404040;
    *(volatile uint32_t *)0xE000E424 = 0x40404040;
    *(volatile uint32_t *)0xE000E428 = 0x40404040;
    *(volatile uint32_t *)0xE000E42C = 0x40404040;
    *(volatile uint32_t *)0xE000E430 = 0x40404040;
    *(volatile uint32_t *)0xE000E434 = 0x40404040;
    *(volatile uint32_t *)0xE000E438 = 0x40404040;
    *(volatile uint32_t *)0xE000E43C = 0x40404040;
    *(volatile uint32_t *)0xE000E440 = 0x40404040;

    hw_gpio_restore();

    hw_clock_init();
    hw_xip_flash_init(true);
    host_hci_reinit();

    ool_write(PMU_REG_PMU_GATE_M, ool_read(PMU_REG_PMU_GATE_M) & (~0x40));

    hw_log_init(true);
    {
        static bool first_wakeup = true;
        bool do_calib = false;
        static TickType_t last_tick;
        TickType_t curr_tick;
        if (first_wakeup) {
            first_wakeup = false;
            last_tick = xTaskGetTickCount();
            curr_tick = last_tick;
            do_calib = true;
        }
        else {
            curr_tick = xTaskGetTickCount();
            if ((curr_tick - last_tick) > 10000) {
                last_tick = curr_tick;
                do_calib = true;
            }
        }
        
        if (do_calib) {
            /* restart calibration */
            __SYSTEM_CALI_CLK_ENABLE();
            cali_handle.mode = CALI_UP_MODE_NORMAL;
            cali_handle.rc_cnt = 60;
            cali_handle.DoneCallback = cali_done_handle;
            cali_init(&cali_handle);
            cali_start_IT(&cali_handle);
            system_prevent_sleep_set(SYSTEM_PREVENT_SLEEP_TYPE_CALIBRATION);
            NVIC_SetPriority(CALI_IRQn, 2);
            NVIC_EnableIRQ(CALI_IRQn);
        }
    }
    
    //gpio_wakeup_func_set(GPIOD, GPIO_PIN_0, current_PD0_level);
}

__RAM_CODE void user_entry_after_sleep_user(void)
{    

	#if (BOARD_SEL == BOARD_EVB_FR3092E_RGB)
        rgb_controller_reinit();
    #else
	    hw_display_init(true);
	#endif
    // hw_touchpad_init(true);
    #if ENABLE_PSRAM
        // hw_psram_init(true);
    #endif


    hw_external_flash_init(true);

    NVIC_SetPriority(PMU_IRQn, 4);
    NVIC_EnableIRQ(PMU_IRQn);
   
    fputc('w', NULL);
   
}

int main( void )
{
    GPIO_InitTypeDef gpio_config;
    UART_HandleTypeDef dsp_uart_handle;
    uint32_t error;
    uint32_t rand_num;
    size_t size;

    system_delay_us(10);

    /* configure all interrupt priority to 2 */
    *(volatile uint32_t *)0xE000E400 = 0x40404040;
    *(volatile uint32_t *)0xE000E404 = 0x40404040;
    *(volatile uint32_t *)0xE000E408 = 0x40404040;
    *(volatile uint32_t *)0xE000E40C = 0x40404040;
    *(volatile uint32_t *)0xE000E410 = 0x40404040;
    *(volatile uint32_t *)0xE000E414 = 0x40404040;
    *(volatile uint32_t *)0xE000E418 = 0x40404040;
    *(volatile uint32_t *)0xE000E41C = 0x40404040;
    *(volatile uint32_t *)0xE000E420 = 0x40404040;
    *(volatile uint32_t *)0xE000E424 = 0x40404040;
    *(volatile uint32_t *)0xE000E428 = 0x40404040;
    *(volatile uint32_t *)0xE000E42C = 0x40404040;
    *(volatile uint32_t *)0xE000E430 = 0x40404040;
    *(volatile uint32_t *)0xE000E434 = 0x40404040;
    *(volatile uint32_t *)0xE000E438 = 0x40404040;
    *(volatile uint32_t *)0xE000E43C = 0x40404040;
    *(volatile uint32_t *)0xE000E440 = 0x40404040;

    pmu_init();

   /* Power Keep: 64KB PRAM, 256KB SRAM */
   pmu_ram_retention_set(PMU_RAM_RET_CACHE_BIT 
                           | PMU_RAM_RET_CRAM0_BIT 
                           | PMU_RAM_RET_CRAM1_BIT 
                           | PMU_RAM_RET_SRAM0_BIT 
                           | PMU_RAM_RET_SRAM1_BIT 
                           | PMU_RAM_RET_SRAM2_BIT
                           | PMU_RAM_RET_SRAM3_BIT
                           | PMU_RAM_RET_SRAM4_BIT
                           );

    /* initial system clock and XIP flash */
    hw_clock_init();
    hw_xip_flash_init(false);

    /* try to handshake with PC to program external flash */
    ext_flash_program(&ext_flash_op, &ext_flash_prog_uart_op);

    /* reinit external flash */
    hw_external_flash_init(false);

    hw_log_init(false);
    
    Uart3_handle.UARTx = UART3;
    Uart3_handle.Init.BaudRate   = 921600;
    Uart3_handle.Init.DataLength = UART_DATA_LENGTH_8BIT;
    Uart3_handle.Init.StopBits   = UART_STOPBITS_1;
    Uart3_handle.Init.Parity     = UART_PARITY_NONE;
    Uart3_handle.Init.FIFO_Mode  = UART_FIFO_ENABLE;
    Uart3_handle.TxCpltCallback  = NULL;
    Uart3_handle.RxCpltCallback  = app_at_rx_done;

    /* do calibration, get current RC frequency */
    __SYSTEM_CALI_CLK_ENABLE();
    cali_handle.mode = CALI_UP_MODE_NORMAL;
    cali_handle.rc_cnt = 200;
    cali_handle.DoneCallback = cali_done_handle;
    cali_init(&cali_handle);
    cali_start_IT(&cali_handle);
    system_prevent_sleep_set(SYSTEM_PREVENT_SLEEP_TYPE_CALIBRATION);
    NVIC_SetPriority(CALI_IRQn, 4);
    NVIC_EnableIRQ(CALI_IRQn);
    
    /* init flashdb to store user data */
    flashdb_init();

    /* get random seed*/
    size = flashdb_get(FDB_KEY_USER_RANDOM_SEED, (void *)&rand_num, 4);
    printf("flashdb get random seed :%d\r\n",size);
    if(size == 0){
        __SYSTEM_TRNG_CLK_ENABLE();
        trng_init();
        trng_read_rand_num((uint8_t *)&rand_num,4);
        flashdb_set(FDB_KEY_USER_RANDOM_SEED,(uint8_t *)&rand_num,4);
        __SYSTEM_TRNG_CLK_DISABLE();
    }
    printf("flash db get rand num: %x\r\n",rand_num);

    printf("flash id : %d \n",IC_W25Qxx_Read_ID());

    /*
     * initialize modules step by step
     * 1. app_task
     * 2. rpmsg
     * 3. btdm stack
     * 4. lvgl
     * 5. others.....
     */

#if ENABLE_RTOS_MONITOR == 1
    xTaskCreate(monitor_task, "monitor", MONITOR_TASK_STACK_SIZE, NULL, tskIDLE_PRIORITY+1, &monitor_task_handle);
#endif
    
//    audio_scene_init(AUDIO_SCENE_TASK_STACK_SIZE, AUDIO_SCENE_TASK_PRIORITY);

    app_task_init();

    /* initialize AT command */
    app_at_init(&Uart3_handle);

    /* 
     * enable pull up of all 3.3v IO, these configuration will be latched by set 
     * BIT6 of PMU_REG_PMU_GATE_M regsiter. used to avoid electric leakage
     */
    SYSTEM->PortA_PullSelect = 0x0000ffff;
    SYSTEM->PortB_PullSelect = 0x00000ff7;
    SYSTEM->PortC_PullSelect = 0x00000000;
    SYSTEM->PortD_PullSelect = 0x0000ffff;
    SYSTEM->PortE_PullSelect = 0x0000ffff;
    SYSTEM->PortA_PullEN = 0x0000ffff;
    SYSTEM->PortB_PullEN = 0x0000ffff;
    SYSTEM->PortC_PullEN = 0x0000ffff;
    SYSTEM->PortD_PullEN = 0x0000ffff;
    SYSTEM->PortE_PullEN = 0x0000ffff;
    SYSTEM->QspiPadConfig.QSPI_PullEN = 0x0000000;

    SYSTEM->PortA_InputOpenCircuit = 0xFFFFFFFF;
    SYSTEM->PortB_InputOpenCircuit = 0xFFFFFFFF;
    SYSTEM->PortC_InputOpenCircuit = 0xFFFFFFFF;
    SYSTEM->PortD_InputOpenCircuit = 0xFFFFFFFF;
    SYSTEM->PortE_InputOpenCircuit = 0xFFFFFFFF;

    /* enable sleep */
    //system_prevent_sleep_clear(SYSTEM_PREVENT_SLEEP_TYPE_DISABLE);
    
    /*PMU_IO0 Wakeup Init */
    // pmu_gpio_int_init(PMU_PIN_2, PMU_GPIO_PULL_UP, 0);
    // pmu_enable_isr(PMU_GPIO_PMU_INT_MSK_BIT); 
    // gpio_wakeup_func_set(GPIOD, GPIO_PIN_0, current_PD0_level);

    printf("FR5090: BTDM test.\r\n");
    printf("sram0x: %x \r\n",ool_read16(PMU_REG_PKSRAM_GATE));

    /* Start the scheduler itself. */
    vTaskStartScheduler();

    return 0;
}

void uart3_irq(void)
{
    uart_IRQHandler(&Uart3_handle);
}

void PMU_GPIO_GROUPH_IRQHandler(void)
{
    current_PD0_level = gpio_read_pin(GPIOD, GPIO_PIN_0);
    gpio_wakeup_ref_level_set(GPIOD, GPIO_PIN_0, current_PD0_level);

    struct app_task_event *event;
    event = app_task_event_alloc(APP_TASK_EVENT_GPIO_PD_WAKEUP, 0, true);
    app_task_event_post(event, false); 
}

#if 0
void PMU_GPIO_PMU_IRQHandler(void)
{
    uint16_t data = ool_read16(PMU_REG_PIN_DATA);
    uint16_t result = ool_read16(PMU_REG_PIN_XOR_RESULT);
    
    /* update last value with latest data */
    ool_write16(PMU_REG_PIN_LAST_V, data);
    /* clear last XOR result */
    ool_write16(PMU_REG_PIN_XOR_CLR, result);
    
    // if (((data & PMU_PIN_2) == 0) && (ool_read(PMU_REG_PIN_XOR_EN) & PMU_PIN_2)) {
    //     struct app_task_event *event;
    //     event = app_task_event_alloc(APP_TASK_EVENT_PMU_WAKEUP, 0, true);
    //     app_task_event_post(event, false);
    // } 
    
    if (data & PMU_PIN_9) {
        system_prevent_sleep_clear(SYSTEM_PREVENT_SLEEP_TYPE_HCI_RX);
    }
    else {
        system_prevent_sleep_set(SYSTEM_PREVENT_SLEEP_TYPE_HCI_RX);
    }
}
#endif

__RAM_CODE void dma0_irq(void)
{
    display_update_dma_isr();
}

__RAM_CODE void dma1_irq(void)
{
    // fputc('/',NULL);
   IC_W25Qxx_DMA_Interrupt();
}

__RAM_CODE void spimx8_1_irq(void)
{
   IC_W25Qxx_Spi_Interrupt();
}

__RAM_CODE void cali_irq(void)
{
    cali_IRQHandler(&cali_handle);
}